package chain

import (
	"context"
	"fmt"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/ethclient"
	"github.com/ethereum/go-ethereum/rpc"
	"log"
	"math"
	"math/big"
	"strconv"
	"strings"
	"time"
	"token-for-token/contract"
	"token-for-token/contract/evmAbi"
	"token-for-token/global"
	"token-for-token/models"
)

func SyncMongoTask() {
	var chainConfig models.ChainConfig
	err := chainConfig.GetChainInfoForRpcUrl(global.BlockChainConfig.RpcUrl)
	var lastBlockNumber int64
	if err != nil {
		log.Println("读取mongo获取最新区块失败, 将从最新区块开始同步")
		number, _ := global.EthRpcClient.BlockNumber(context.Background())
		lastBlockNumber = int64(number) - 3
	} else {
		lastBlockNumber = chainConfig.LastBlockNumber
		//lastBlockNumber = 40812635
	}

	for i := lastBlockNumber; true; i++ {
		// 扫太快了 兑换申请没有添加到数据库中 导致归集不到
		number, err := global.EthRpcClient.BlockNumber(context.Background())
		if err != nil {
			log.Println("获取最新区块失败, 1秒后重试", err)
			time.Sleep(1 * time.Second)
			continue
		}
		if int64(number)-i <= global.BlockChainConfig.Diff {
			//log.Println("等待区块生成  ============= blockNumber", i)
			i--
			time.Sleep(2 * time.Second)
			continue
		}
		block, err := global.EthRpcClient.BlockByNumber(context.Background(), big.NewInt(i))
		if err != nil {
			// 马蹄链专属傻逼错误
			if err.Error() == "server returned non-empty transaction list but block header indicates no transactions" {
				log.Println("server returned non-empty transaction list but block header indicates no transactions ============= blockNumber", i)
				continue
			}

			if err.Error() == "not found" {
				log.Println("等待区块生成  ============= blockNumber", i)
				i--
				time.Sleep(1 * time.Second)
				continue
			}
			log.Printf("获取 %v 区块失败: %v", i, err)
			i--
			time.Sleep(1 * time.Second)
			continue
		}

		//global.RedisDB.Set(context.Background(), "lastBlockNumber", i, 0)
		// 存储到mongo
		err = chainConfig.UpdateChainConfigLastBlockNumber(i)
		if err != nil {
			log.Printf("更新 %s 区块失败: %v", i, err)
		}
		hash := block.Hash()
		num := int64(block.NumberU64())
		var blockNumberOrHash rpc.BlockNumberOrHash
		blockNumberOrHash.BlockHash = &hash
		blockNumberOrHash.BlockNumber = (*rpc.BlockNumber)(&num)
		blockNumberOrHash.RequireCanonical = true
		receipts, _ := global.EthRpcClient.BlockReceipts(context.Background(), blockNumberOrHash)

		go CollectionOperation(block.Transactions(), block.Number().Int64(), receipts)
		time.Sleep(1 * time.Second)
	}
}

func CollectionOperation(transactions types.Transactions, blockNumber int64, receipts []*types.Receipt) {
	if len(transactions) == 0 {
		return
	}
	receiptsArr := make(map[string]*types.Receipt)
	for _, receipt := range receipts {
		receiptsArr[receipt.TxHash.Hex()] = receipt
	}

	// 获取监听地址
	var chainUserConfig models.ChainUserConfig
	for _, transaction := range transactions {
		if transaction.To() == nil {
			continue
		}

		//if transaction.Hash().Hex() == "0x07e8c4623f81dc490035720aa01c192c7f9e15b1b214f2fcdb21ba1fffc78fb8" {
		//	log.Println("收到兑换申请", transaction.Hash().Hex())
		//}

		parsedAbi, err := abi.JSON(strings.NewReader(evmAbi.ERC20_ABI))
		//用abi解析data
		method, err := parsedAbi.MethodById(transaction.Data())
		if err != nil {
			continue
		}
		//是否为transfer
		if method.Name != "transfer" {
			continue
		}
		var result struct {
			To     common.Address
			Amount *big.Int
		}
		result1, err := method.Inputs.Unpack(transaction.Data()[4:])
		if err != nil {
			fmt.Printf("解析data错误: %v\n", err)
			continue
		}
		result.To = result1[0].(common.Address)
		result.Amount = result1[1].(*big.Int)

		// 判断是否为监听地址
		err = chainUserConfig.GetChainUserConfigForCollectAddress(result.To.Hex())
		if err != nil {
			continue
		}

		// 根据hash获取兑换申请
		var chainApply models.ChainApply
		err = chainApply.GetChainApplyInfoForId(transaction.Hash().String())
		if err != nil {
			continue
		}

		// 可能为私有链缺少eth_getBlockReceipts方法 导致无法获取所有交易凭证  单独处理
		if len(receiptsArr) == 0 {
			receipt, err := global.EthRpcClient.TransactionReceipt(context.Background(), transaction.Hash())
			if err != nil {
				log.Println("获取交易凭证失败: ", transaction.Hash().Hex())
				continue
			}
			receiptsArr[transaction.Hash().Hex()] = receipt
		}
		// 判断是否为交易成功
		receipt, ok := receiptsArr[transaction.Hash().Hex()]
		if !ok && receipt.Status != 1 {
			log.Println("失败的交易,我愿称之为搞事的hash: ", transaction.Hash().Hex())
			// 修改状态兑换申请状态
			err = chainApply.UpdateChainApplyStatus("3")
			continue
		}

		// 判断用户是否到期
		var user models.User
		err = user.GetUserInfoForId(chainUserConfig.UserId)
		if err != nil {
			log.Println("用户不存在: ", chainUserConfig.UserId)
			continue
		}
		if user.TokenForTokenMaturityTime < time.Now().Unix() {
			log.Println("用户到期: ", chainUserConfig.UserId)
			// 修改状态兑换申请状态
			err = chainApply.UpdateChainApplyStatus("2")
			if err != nil {
				log.Println("修改申请状态为用户已过期(2)失败:", err, "=====hash:", transaction.Hash().Hex())
			}
			log.Println("已修改申请状态为 2 : ", transaction.Hash().Hex())
			continue
		}

		// 获取代币信息
		var tokenInfo models.ChainToken
		err = tokenInfo.GetChainTokenInfoForId(chainApply.TokenId0)
		if err != nil {
			log.Printf("获取代币信息错误: %v\n", err)
			continue
		}

		// 计算金额
		divisor := big.NewInt(10).Exp(big.NewInt(10), big.NewInt(int64(tokenInfo.Decimals)), nil)
		dividendBig, _ := new(big.Int).SetString(result.Amount.String(), 10)
		expression := new(big.Rat).SetInt(dividendBig).Quo(new(big.Rat).SetInt(dividendBig), new(big.Rat).SetInt(divisor))
		resultFloat, _ := expression.Float64()
		quotient := strconv.FormatFloat(resultFloat, 'f', -1, 64)

		log.Printf("监听地址:%s 进入充币 金额: %s 块高度: %v \n", result.To.String(), quotient, blockNumber)

		// 查询chain1信息
		var chain1Info models.ChainConfig
		err = chain1Info.GetChainInfoForId(chainApply.ChainConfigId1)
		if err != nil {
			log.Printf("获取chain1信息错误: %v\n", err)
			continue
		}

		// 开始兑换
		var chain1EthClient *ethclient.Client
		chain1EthClient, err = ethclient.Dial(chain1Info.RpcUrl)
		if err != nil {
			log.Println("链1连接错误 error : ", err)
			continue
		}

		// 根据链1地址获取私钥
		err = chainUserConfig.GetChainUserConfigForChainIdUserId(chainApply.ChainConfigId1, user.Id.Hex())
		if err != nil {
			log.Println("链1获取用户配置错误 error : ", err)
			continue
		}
		//privateKeyUpCoin, err := crypto.HexToECDSA(chainUserConfig.FeeAddress)
		//if err != nil {
		//	log.Printf("获取私钥错误: %s", err)
		//	continue
		//}

		// 获取token1信息
		var token1Info models.ChainToken
		err = token1Info.GetChainTokenInfoForId(chainApply.TokenId1)
		if err != nil {
			log.Printf("获取token1信息错误: %v\n", err)
			continue
		}

		chainToChain := new(models.ChainToChain)
		chainToChain.Token0 = chainApply.TokenId0
		chainToChain.Token1 = chainApply.TokenId1
		chainToChain.UserId = chainApply.UserId
		chainToChain.ChainConfigId0 = chainApply.ChainConfigId0
		chainToChain.ChainConfigId1 = chainApply.ChainConfigId1
		err = chainToChain.GetInfoByParams()
		if err != nil {
			log.Printf("获取链信息配置错误: %v\n", err)
			continue
		}
		// 解析为 float64
		floatValue, err := strconv.ParseFloat(chainToChain.Rate.String(), 64)
		if err != nil {
			log.Printf("primitive.Decimal128转float64错误: %v\n", err)
			continue
		}
		// 扣除手续费
		quotient = strconv.FormatFloat(resultFloat-floatValue, 'f', -1, 64)
		// 根据token1精度将quotient转换为token1精度
		amountBig := new(big.Float)
		amountBig.SetString(quotient)
		amountBig.Mul(amountBig, big.NewFloat(math.Pow10(int(token1Info.Decimals))))
		amountBigInt, _ := amountBig.Int(nil)

		//txgj, err := contract.SandToken(chain1EthClient, privateKeyUpCoin, common.HexToAddress(token1Info.Address), common.HexToAddress(chainApply.To), amountBigInt)

		txgj, err := contract.SandToken(chain1EthClient, chainUserConfig.BusinessId, common.HexToAddress(chainApply.To).Hex(), chain1Info.RpcUrl, amountBigInt.String(), common.HexToAddress(token1Info.Address).Hex())

		if err != nil {
			log.Printf("兑换失败: %s", err)
			// 修改状态兑换申请状态
			err = chainApply.UpdateChainApplyStatus("4")
			if err != nil {
				log.Println("修改申请状态为兑换失败(4) 失败:", err, "=====hash:", transaction.Hash().Hex())
			}
			log.Println("已修改申请状态为 4 : ", transaction.Hash().Hex())
			continue
		}
		// 修改状态兑换申请状态
		err = chainApply.UpdateChainApplyStatus("1")
		if err != nil {
			log.Println("修改申请状态为兑换成功(1) 失败:", err, "=====hash:", transaction.Hash().Hex())
		}
		log.Println("已修改申请状态为兑换成功(1) : ", transaction.Hash().Hex())
		log.Println("兑换成功,hash:", txgj.Hash().String(), "==token:", token1Info.Address, "==to:", chainApply.To, "==value:", quotient)
	}

}
